package com.whity.towerdefense.model;

import javax.microedition.khronos.opengles.GL10;

import android.graphics.Point;
import android.graphics.PointF;

import com.whity.towerdefense.view.GraphicDrawable;
import com.whity.towerdefense.view.GraphicElt;

public class Unit implements GraphicDrawable, I_Tickable, I_Pathfindable, I_Targetable
{
	// =============================================================
	// Private attributs :
	
	// General attributs
	private final String 			m_TypeName;
	private final GraphicElt		m_GraphicElt;
	
	// Statistic originale: Ori = Original
	private final float				m_OriLife;
	private final float				m_OriSpeed;		// in Cell per second
	private final float				m_OriArmor;
	private final int				m_OriBounty;
	private final float				m_OriDamage;
	private final float				m_OriRange;
	
	private final Map				m_Map;
	private Cell					m_CurrentCell;
	
	// Current statistics
	private float					m_Life;	
	
	// Current attributs
	private PointF					m_Pos;		// In cell, in the repere of the map.
	private Path					m_Path;
	
	// =============================================================
	// Protected functions :
	
	public Unit(String p_TypeName, float p_Life, float p_Speed, float p_Armor, int p_Bounty, float p_Damage, float p_Range, float p_Size, GraphicElt p_GraphicElt, float p_X, float p_Y)
	{
		m_TypeName		= p_TypeName;
		m_GraphicElt	= p_GraphicElt;
		m_OriLife		= m_Life = p_Life;
		m_OriSpeed		= p_Speed;
		m_OriArmor		= p_Armor;
		m_OriBounty		= p_Bounty;
		m_OriDamage		= p_Damage;
		m_OriRange		= p_Range;
		m_Path			= null;
		
		m_Pos 			= new PointF(p_X, p_Y);
		m_GraphicElt.SetScale(p_Size/(m_GraphicElt.GetWidth()/Map.UNIT_SIZE));
		m_GraphicElt.SetPosition(m_Pos.x * Map.UNIT_SIZE - m_GraphicElt.GetWidth()*m_GraphicElt.GetScaleX()/2, m_Pos.y * Map.UNIT_SIZE - m_GraphicElt.GetHeight()*m_GraphicElt.GetScaleY()/2);
		
		m_Map 			= GameModel.LEVEL.GetMap();
		int cellIndex = (int) (m_Pos.x + m_Pos.y*m_Map.GetWidthInCells());
		m_CurrentCell = m_Map.GetCells().get(cellIndex);
		m_CurrentCell.SetUnit(this);
		
		
		// We calculate the path of the unit until the goal
		GameModel.LEVEL.CalculatePath(this, GameModel.LEVEL.GetMap().GetGoal());
	}
	
	public Unit(String p_TypeName, float p_Life, float p_Speed, float p_Armor, int p_Bounty, float p_Damage, float p_Range, float p_Size, GraphicElt p_GraphicElt, float p_X, float p_Y, Path p_Path)
	{
		this(p_TypeName, p_Life, p_Speed, p_Armor, p_Bounty, p_Damage, p_Range, p_Size, p_GraphicElt, p_X, p_Y);	
		m_Path			= p_Path;
	}
	
	// =============================================================
	// Public functions

	public void Draw(GL10 p_GL)
	{
		m_GraphicElt.Draw(p_GL);
	}

	public boolean GameTick(int p_Ms)
	{
		float walkDistance = (m_OriSpeed/1000.0f) * p_Ms;
		
		if(m_Life <= 0)
		{
			return false;
		}
		
		if(m_Path != null)
		{
			synchronized(m_Path)
			{
				PointF dest = m_Path.GetNextPoint();
				if(dest != null)
				{
					PointF direction = new PointF(dest.x-m_Pos.x, dest.y-m_Pos.y);
					float dest_distance = (float) Math.sqrt(direction.x*direction.x+direction.y*direction.y);
					
					if(dest_distance < 0.05)
					{
						m_Path.PointReached();
					}
					
					float ratio;
					if(dest_distance < walkDistance)
					{
						walkDistance = dest_distance;
						ratio = 1.0f;
					}
					else
					{
						ratio = (float)(walkDistance/dest_distance);
					}
					
					int floorX = (int) (Math.floor(m_Pos.x + direction.x*ratio) - Math.floor(m_Pos.x));
					int floorY = (int) (Math.floor(m_Pos.y + direction.y*ratio) - Math.floor(m_Pos.y));
					
					m_Pos.x += direction.x*ratio;
					m_Pos.y += direction.y*ratio;
					
					if(floorX != 0 || floorY != 0)
					{
						m_CurrentCell.RemoveUnit(this);
						
						int cellIndex = (int) (m_Pos.x + m_Pos.y*m_Map.GetWidthInCells());
						m_CurrentCell = m_Map.GetCells().get(cellIndex);
						
						m_CurrentCell.SetUnit(this);
					}
				}
				m_GraphicElt.SetPosition(m_Pos.x * Map.UNIT_SIZE - m_GraphicElt.GetWidth()*m_GraphicElt.GetScaleX()/2, m_Pos.y * Map.UNIT_SIZE - m_GraphicElt.GetHeight()*m_GraphicElt.GetScaleY()/2);
			}
		}
		
		return true;
	}
	
	public void SetPosition(float p_X, float p_Y)
	{
		m_Pos.x = p_X;
		m_Pos.y = p_Y;
	}
	
	public PointF GetPosition()
	{
		return m_Pos;
	}
	
	public Point GetCellPosition()
	{
		Point res = new Point();
		res.x = (int) Math.floor(m_Pos.x);
		res.y = (int) Math.floor(m_Pos.y);
		return res;
	}
	
	public void PathfinderCallback(Path p_Path)
	{
		if(m_Path != null)
		{
			synchronized(m_Path)
			{
				m_Path = p_Path;
			}
		}
		else
		{
			m_Path = p_Path;
		}
	}

	public void OnHit(Shoot p_Shoot)
	{
		m_Life -= p_Shoot.GetDamage();
		if(m_Life <= 0)
		{
			GameModel.LEVEL.RemoveUnit(this);
		}
	}
	
	// =============================================================
}
